package impl

import (
	"time"

	"hundsun.com/hsl/hschain/common/types"
	"hundsun.com/hsl/hschain/protos/p2p"
	"hundsun.com/hsl/hschain/store/mq"
)

// FetchBlock 根据指定区块号拉取区块
func (chain *BlockChain) FetchBlock(start uint64, end uint64) (err error) {
	if chain.topicClient == nil {
		chain.log.Error("FetchBlock chain topic client not bind message queue.")
		return types.ErrMqClientNotReady
	}

	chain.log.Debugf("FetchBlock input, StartHeight: %d and EndHeight: %d.", start, end)
	if end < start {
		return types.ErrEndLessThanStart
	}
	blockcount := end - start

	var requestblock p2p.GetBlocksRequest
	requestblock.Start = start

	//同步block一次请求128个
	if blockcount >= chain.maxFetchBlockNum {
		requestblock.End = start + chain.maxFetchBlockNum - 1
	} else {
		requestblock.End = end
	}

	chain.log.Infof("FetchBlock: [%d,%d]", requestblock.Start, requestblock.End)
	msg := chain.topicClient.NewMessage(types.P2pModule, mq.ModeTopic.String(), types.EventFetchBlock, &requestblock)
	Err := chain.topicClient.Send(msg, false)
	if Err != nil {
		chain.log.Error("FetchBlock", "client.Send err:", Err)
		return err
	}
	return nil
}

// SyncBlocksFromPeers blockSynSeconds时间检测一次本节点的height是否有增长，没有增长就需要通过对端peerlist获取最新高度，发起同步
func (chain *BlockChain) SyncBlocksFromPeers() {
	curheight := chain.GetBlockHeight()
	peerMaxBlkHeight := chain.GetPeerMaxBlkHeight()

	//获取peers的最新高度.处理没有收到广播block的情况
	//落后超过2个区块时主动同步区块，落后一个区块时需要判断是否超时
	needSync := curheight < peerMaxBlkHeight
	chain.log.Infof("Get peer max block height, current %d and peer max %d.", curheight, peerMaxBlkHeight)

	if needSync {
		chain.log.Infof("SynBlocksFromPeers, curheight: %d and peerMaxBlkHeight: %d", curheight, peerMaxBlkHeight)
		err := chain.FetchBlock(curheight+1, peerMaxBlkHeight)
		if err != nil {
			chain.log.Error("SynBlocksFromPeers FetchBlock", "err", err)
		}
	}
}

// FetchPeerList 拉取Peer列表信息
func (chain *BlockChain) FetchPeerList() error {
	if chain.topicClient == nil {
		chain.log.Error("fetchPeerList chain client not bind message queue.")
		return nil
	}
	msg := chain.workerClient.NewMessage(types.P2pModule, mq.ModeWorker.String(), types.EventPeerInfo, nil)
	err := chain.workerClient.Send(msg, true)
	if err != nil {
		chain.log.Errorf("send fetchPeerList msg failed, err: %s", err.Error())
		return err
	}

	resp, err := chain.workerClient.WaitTimeout(msg, 5*time.Second)
	if err != nil {
		chain.log.Errorf("Fetch peer list wait response failed, err:%s\n", err.Error())
		return err
	}

	peerInfos := (resp.Data).(*p2p.PeerInfos)
	err = chain.ProcUpdatePeerInfo(peerInfos)
	if err != nil {
		chain.log.Errorf("ProcUpdatePeerInfo failed, err:%s", err.Error())
	} else {
		chain.log.Debugf("ProcUpdatePeerInfo success, got %d peer infos.", len(peerInfos.Peers))
	}

	return nil
}

// SyncNtp 同步NTP信息
func (chain *BlockChain) SyncNtp() {
}

// ProcUpdatePeerInfo 处理Peer信息更新
func (chain *BlockChain) ProcUpdatePeerInfo(peerInfos *p2p.PeerInfos) error {
	curheigt := chain.GetBlockHeight()

	var peerInfoList p2p.PeerInfos
	for _, peer := range peerInfos.GetPeers() {
		//过滤掉自己和小于自己5个高度的节点
		if peer == nil || peer.Self || peer.Header == nil || (peer.Header != nil && curheigt > peer.Header.Number+5) {
			continue
		}
		var peerInfo p2p.PeerInfo
		peerInfo.Name = peer.Name
		peerInfo.Version = peer.Version
		peerInfo.Header = peer.Header

		peerInfoList.Peers = append(peerInfoList.Peers, &peerInfo)
	}
	//peerlist中没有比自己节点高的就不做处理直接返回
	if len(peerInfoList.Peers) == 0 {
		return nil
	}

	chain.peerList = &p2p.PeerInfos{Peers: peerInfoList.Peers}
	return nil
}

// GetPeerMaxBlkHeight 获取peer列表中最大高度
func (chain *BlockChain) GetPeerMaxBlkHeight() uint64 {
	var maxHeight uint64

	if chain.peerList != nil && chain.peerList.Peers != nil {
		maxHeight = chain.peerList.Peers[0].Header.Number
		peerlen := len(chain.peerList.Peers)
		for i := peerlen - 1; i >= 0; i-- {
			chain.log.Infof("Peer[%s] heihgt %d", chain.peerList.Peers[i].Name, chain.peerList.Peers[i].Header.Number)
			peerHeader := chain.peerList.Peers[i].Header
			if peerHeader != nil {
				currentPeerHeight := peerHeader.Number
				if currentPeerHeight > maxHeight {
					maxHeight = currentPeerHeight
				}
			}
		}
	}
	return maxHeight
}
